﻿using ImageK.Gui;
using ImageK.Java;
using ImageK.Measure;
using ImageK.Plugin.Filter;
using ImageK.Process;
using ImageK;
using System;
using System.Collections.Generic;
using System.IO.Packaging;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;

namespace ImageK.Plugin.Filter
{
    /** This plugin implements ImageJ's Fill, Clear, Clear Outside and Draw commands. */
    public class Filler : Measurements, PlugInFilter
    {
        String arg;
        Roi roi;
        ImagePlus imp;
        int sliceCount;
        ImageProcessor mask;
        bool isTextRoi;

        public int setup(String arg, ImagePlus imp)
        {
            this.arg = arg;
            this.imp = imp;
            if (imp != null)
                roi = imp.getRoi();
            isTextRoi = roi != null && (roi is TextRoi);
            if (isTextRoi && (arg.Equals("draw") || arg.Equals("fill")) && ((TextRoi)roi).getAngle() != 0.0)
            {
                String s = IJ.isMacOSX() ? "command+b" : "ctrl+b";
                IJ.error("Draw rotated text by pressing " + s + " (Image>Overlay>Add Selection).");
                return BasePlugInFilter.DONE;
            }

            IJ.register(typeof(Filler));
            int baseCapabilities = BasePlugInFilter.DOES_ALL + BasePlugInFilter.ROI_REQUIRED;
            if (arg.Equals("clear"))
            {
                if (roi != null && roi.getType() == Roi.POINT)
                {
                    IJ.error("Clear", "Area selection required");
                    return BasePlugInFilter.DONE;
                }

                if (isTextRoi || isLineSelection())
                    return baseCapabilities;
                else
                    return IJ.setupDialog(imp, baseCapabilities + BasePlugInFilter.SUPPORTS_MASKING);
            }
            else if (arg.Equals("draw"))
                return IJ.setupDialog(imp, baseCapabilities);
            else if (arg.Equals("label"))
            {
                if (Analyzer.firstParticle < Analyzer.lastParticle)
                    return baseCapabilities - BasePlugInFilter.ROI_REQUIRED;
                else
                    return baseCapabilities;
            }
            else if (arg.Equals("outside"))
                return IJ.setupDialog(imp, baseCapabilities);
            else if (roi != null && roi.getType() == Roi.POINT && arg.Equals("fill"))
            {
                IJ.error("Fill", "Area selection required");
                return BasePlugInFilter.DONE;
            }
            else
                return IJ.setupDialog(imp, baseCapabilities + BasePlugInFilter.SUPPORTS_MASKING);
        }

        public void run(ImageProcessor ip)
        {
            if (arg.Equals("clear"))
                clear(ip);
            else if (isTextRoi && (arg.Equals("draw") || arg.Equals("fill")))
                draw(ip);
            else if (arg.Equals("fill"))
                fill(ip);
            else if (arg.Equals("draw"))
                draw(ip);
            else if (arg.Equals("label"))
                label(ip);
            else if (arg.Equals("outside"))
                clearOutside(ip);
        }

        bool isLineSelection()
        {
            return roi != null && roi.isLine();
        }

        bool isStraightLine()
        {
            return roi != null && roi.getType() == Roi.LINE;
        }

        public void clear(ImageProcessor ip)
        {
            ip.setGlobalBackgroundColor();
            if (isLineSelection())
            {
                if (isStraightLine() && roi.getStrokeWidth() > 1)
                    ip.fillPolygon(roi.getPolygon());
                else
                    roi.drawPixels();
            }
            else if (roi != null && roi is TextRoi)
                ((TextRoi)roi).clear(ip);
            else
                ip.fill(); // fill with background color

            ip.setGlobalForegroundColor();
        }

        /**
	    * @deprecated
	    * replaced by ImageProcessor.fill(Roi)
	    */
        public void fill(ImageProcessor ip)
        {
            if (!IJ.isMacro() || !ip.fillValueSet())
                ip.setGlobalForegroundColor();
            if (isLineSelection())
            {
                if (isStraightLine() && roi.getStrokeWidth() > 1 && !(roi is Arrow))
                {
                    Roi roi2 = Roi.convertLineToArea(roi);
                    ip.setRoi(roi2);
                    ip.fill(roi2.getMask());
                    ip.setRoi(roi);
                }
                else
                    roi.drawPixels(ip);
            }
            else
                ip.fill(); // fill with foreground color
        }

        /**
	    * @deprecated
	    * replaced by ImageProcessor.draw(Roi)
	    */
        public void draw(ImageProcessor ip)
        {
            ip.setGlobalForegroundColor();
            roi.drawPixels(ip);
            if (IJ.altKeyDown())
                drawLabel(ip);
        }

        public void label(ImageProcessor ip)
        {
            if (!IJ.isMacro())
            {
                IJ.error("Label",
                    "To label a selection, enable \"Add to overlay\" in Analyze>\nSet Measurements and press 'm' (Analyze>Measure).");
                return;
            }

            if (Analyzer.getCounter() == 0)
            {
                IJ.error("Label", "Measurement counter is zero");
                return;
            }

            if (Analyzer.firstParticle < Analyzer.lastParticle)
                drawParticleLabels(ip);
            else
            {
                ip.setColor(ToolBar.getForegroundColor());
                ImageCanvas ic = imp.getCanvas();
                if (ic != null)
                {
                    double mag = ic.getMagnification();
                    if (mag < 1.0)
                    {
                        int lineWidth = 1;
                        lineWidth = (int)(lineWidth / mag);
                        ip.setLineWidth(lineWidth);
                    }
                }

                roi.drawPixels(ip);
                ip.setLineWidth(1);
                drawLabel(ip);
            }
        }

        void drawParticleLabels(ImageProcessor ip)
        {
            ResultsTable rt = ResultsTable.getResultsTable();
            int count = rt.size();
            int first = Analyzer.firstParticle;
            int last = Analyzer.lastParticle;
            if (count == 0 || first >= count || last >= count)
                return;
            if (!rt.columnExists(ResultsTable.X_CENTROID))
            {
                IJ.error("Label", "\"Centroids\" required to label particles");
                return;
            }

            for (int i = first; i <= last; i++)
            {
                int x = (int)rt.getValueAsDouble(ResultsTable.X_CENTROID, i);
                int y = (int)rt.getValueAsDouble(ResultsTable.Y_CENTROID, i);
                drawLabel(imp, ip, i + 1, new Rectangle(x, y, 0, 0));
            }
        }

        void drawLabel(ImageProcessor ip)
        {
            int count = Analyzer.getCounter();
            if (count > 0 && roi != null)
                drawLabel(imp, ip, count, roi.getBounds());
        }

        public void drawLabel(ImagePlus imp, ImageProcessor ip, int count, Rectangle r)
        {
            Color foreground = ToolBar.getForegroundColor();
            Color background = ToolBar.getBackgroundColor();
            if (foreground.Equals(background))
            {
                foreground = Color.Black;
                background = Color.White;
            }

            int size = 9;
            ImageCanvas ic = imp.getCanvas();
            if (ic != null)
            {
                double mag = ic.getMagnification();
                if (mag < 1.0)
                    size = (int)(size / mag);
            }

            if (size == 9 && r.Width > 50 && r.Height > 50)
                size = 12;
            ip.setFont(new Font("SansSerif", size, FontStyle.Regular));
            String label = "" + count;
            int w = ip.getStringWidth(label);
            int x = r.X + r.Width / 2 - w / 2;
            int y = r.Y + r.Height / 2 + Math.Max(size / 2, 6);
            FontMetrics metrics = ip.getFontMetrics();
            int h = metrics.getHeight();
            ip.setColor(background);
            ip.setRoi(x - 1, y - h + 2, w + 1, h - 3);
            ip.fill();
            ip.resetRoi();
            ip.setColor(foreground);
            ip.drawString(label, x, y);
        }

        /**
        * @deprecated
        * replaced by ImageProcessor.fillOutside(Roi)
        */
        public void clearOutside(ImageProcessor ip)
        {
            if (isLineSelection())
            {
                IJ.error("\"Clear Outside\" does not work with line selections.");
                return;
            }

            sliceCount++;
            Rectangle r = ip.getRoi();
            if (mask == null)
                makeMask(ip, r);
            ip.setGlobalBackgroundColor();
            int stackSize = imp.getStackSize();
            if (stackSize > 1)
                ip.snapshot();
            ip.fill();
            ip.reset(mask);
            int width = ip.getWidth();
            int height = ip.getHeight();
            ip.setRoi(0, 0, r.X, height);
            ip.fill();
            ip.setRoi(r.X, 0, r.Width, r.Y);
            ip.fill();
            ip.setRoi(r.X, r.Y + r.Height, r.Width, height - (r.Y + r.Height));
            ip.fill();
            ip.setRoi(r.X + r.Width, 0, width - (r.X + r.Width), height);
            ip.fill();
            ip.setRoi(r); // restore original ROI
            if (sliceCount == stackSize)
            {
                ip.setGlobalForegroundColor();
                Roi roi = imp.getRoi();
                imp.deleteRoi();
                imp.updateAndDraw();
                imp.setRoi(roi);
            }
        }

        public void makeMask(ImageProcessor ip, Rectangle r)
        {
            mask = ip.getMask();
            if (mask == null)
            {
                mask = new ByteProcessor(r.Width, r.Height);
                mask.invert();
            }
            else
            {
                // duplicate mask (needed because getMask caches masks)
                mask = mask.duplicate();
            }

            mask.invert();
        }
    }
}